現在打開任何一間求職網站,搜尋前端相關職缺,幾乎每間公司的職缺都會在求職條件或加分項目中提到目前的三大框架:React、Vue、Angular;或著也可以到 GitHub 上搜尋 Awesome Javascript,其中的 Framework 數量也是琳瑯滿目,其中也能看到三大框架驚人的星星數量;這麼熱門的技術,身為開發者的我們好像不跟著用也不太對,但到底為什麼好像全世界都在用「框架」呢?
剛好三框架三色完美契合 XD
本系列文已經重新編校彙整編輯成冊,並正式出版囉!
《前端三十:從 HTML 到瀏覽器渲染的前端開發者必備心法》好評販售中!
喜歡我文章內容的讀者們,歡迎您 前往購買 支持!
先回想一下在沒有框架的時候,開發者的日常會長什麼樣子吧。
那時最流行的「套件」非 jQuery 莫屬了,語法簡潔、直覺好用的元素選取器,語法便捷的事件監聽註冊,豐富的開發者生態系產出大量的開源套件,以及最重要的,弭平瀏覽器 XHR 差異的 ajax
函式,將複雜的瀏覽器差異藏在套件中,讓開發者只需要專注在想實作的邏輯即可。
透過好用的元素選取器及事件監聽,開發者們可以依據使用者的行為,靈活操作 DOM 元素的結構及樣式,jQuery 的這些特色直到 2019 的現在,都仍是很實用的功能,甚至 程式碼本身 也有許多值得開發者學習的地方;但隨著專案規模逐漸增大,程式複雜度不斷上升,直接操作 DOM 的缺點也就逐漸浮出檯面:
HTML、CSS、JavaScript 無法維持原先的各司其職,因為需要透過 JavaScript 處理互動內容,勢必要將結構、樣式寫到 JavaScript 的部份中,也就因此造成架構耦合度提高,程式碼管理困難。
在 本系列文的第三篇:Reflow 及 Repaint 是什麼? 中有提到 Render Tree 的概念,當 DOM 被改變,勢必要觸發整個 Reflow & Repaint 的流程,頻繁的改動觸發重複渲染,便會讓頁面效能被消耗殆盡。
既然有前述的問題,自然會有強者試圖解決它。歷史進程中有許多大神拋出了方法論,或是實作出解決方案,但這中間的歷史礙於篇幅,就容筆者簡略帶過吧。總之,現今的「框架」其實就是一種提升開發效率、降低維護難度的開發架構。主流的框架如 React、Vue 等,大都擁有這些特性:
在原先,我們想要顯示一筆資料,可能必須寫死在 HTML 的結構中:
<div id="root">
<div class="card">
<h3 class="name">Gary</h3>
<p class="comment">something</p>
</div>
</div>
若是動態的資料,可能要透過 JavaScript 將資料動態塞入 DOM 元素上:
let commentData = [
{ name: 'Gary', comment: 'try harder'},
{ name: 'Alice', comment: 'good'},
{ name: 'Bob', comment: 'excellent'},
//...
]
commentData.forEach(c => {
$('#root').append(
$(document.createElement('div')).append(
$(document.createElement('h3')).text(c.name),
$(document.createElement('p')).text(c.comment)
).addClass('card')
)
})
但在 Vue 中,可能就會寫成這樣:
<template>
<div id="root">
<div class="card" v-for="item in commentData" :key="name">
<h3 class="name">{{item.name}}</h3>
<p class="comment">{{item.comment}}</p>
</div>
</div>
</template>
<script>
export default {
data() {
return {
commentData: [
{ name: 'Gary', comment: 'try harder'},
{ name: 'Alice', comment: 'good'},
{ name: 'Bob', comment: 'excellent'},
//...
]
}
}
}
</script>
乍看之下兩種寫法好似相差無幾,但如果考慮到可讀性及後續的可維護性,相信大部分讀者都能同意後者相對來說會比較容易閱讀及維護吧?透過 Vue 的寫法,讓頁面結構的部份仍然由 HTML 決定,並由資料來決定畫面該如何呈現。
注意到關鍵了嗎? 由資料決定畫面 ,這就是現代框架至關重要的核心概念;將資料從介面中抽取出來,每個畫面都是對資料處理過後的呈現。
一個網站總是會有一些重複出現的元素,例如按鈕、輸入表單、表格、對話框等等,像是 FaceBook 的按讚按鈕,可能一個頁面出現數十個都不為過;而在現代框架的概念中,我們會把這些重複出現的元素稱為 組件(Components),每個組件內包含了組件自己需要用的結構、樣式、邏輯。例如前述的例子,我們可以將 .card
這個元素抽成單獨的組件:
<template>
<div class="card">
<h3 class="name">{{name}}</h3>
<p class="comment"></p>{{comment}}</p>
</div>
</template>
<script>
export default {
name: 'Card',
props: {
name: [String],
comment: [String]
}
}
</script>
再從外部組件引入它:
<template>
<div id="root">
<Card v-for="item in commentData" v-bind:"item" :key="name" />
</div>
</template>
<script>
import Card from './components/Card'
export default {
components: {
Card
},
data() {
return {
commentData: [
{ name: 'Gary', comment: 'try harder'},
{ name: 'Alice', comment: 'good'},
{ name: 'Bob', comment: 'excellent'},
//...
]
}
}
}
</script>
這樣一來,各組件只需要處理組件內的事,外部引用的組件來決定怎麼使用、提供什麼資料給組件,藉由簡單的切分權責,加上前述的由資料決定畫面,就能讓各個組件的任務單一,並且能被重複使用。
甚至現在有許多完成度很高的前端 UI 組件庫,讓開發者有如玩樂高一樣,能快速地用組件堆出想要的畫面,大幅度降低了組織畫面所需要的時間,讓工程師能更專注在處理商業邏輯上。
如同前述,在複雜的頁面中,如果頻繁透過操作 DOM 的方式改變畫面,可能會造成全頁面的 Reflow 及 Repaint;不過在使用框架時,開發者不用太擔心這個問題。
原因是在各主流框架的實作中,幾乎都包含了 Virtual Dom 的概念,也就是用 JavaScript 物件來表達當前的頁面結構;藉由與 UI 分離的資料及 Virtual Dom 之間的關係,當資料變動時,事先計算好這次畫面需要變動的地方,如此一來便能抵銷掉無意義的更動,並重複利用已存在的 DOM 元素;當真的要進行 DOM 更新時,也會一次將所有需要更新的局部組件更新,讓效能的耗損盡可能降低。
當然,Virtual Dom 變動的計算也是需要耗費運算資源的,若是過於簡單的專案,或是批次的大量修改,很有可能原生的寫法還比框架快上不少;有興趣深入理解的同學可以參考 這篇文章。
前述的眾多問題,若開發者能深入理解箇中邏輯,大都也能自行實作出類似功能的自訂框架;但選用熱門框架的優點,還包含了能參與豐富的開發者生態圈;各大主流框架,在 GitHub 上都能輕鬆的找到數以千計的眾多套件,從基本組件、互動效果,到快速建立整個專案的模板,你想解決的問題也許剛好有別人早已經解決過,如果合適,自然可以從中擷取前人的智慧,更進一步的增進開發效率;若正巧,你的需求沒有人做過,你也可以跳出來,成為那個實作出來的「沒有人」。
現代的瀏覽器已經逐漸符合標準規範,使得 jQuery 的重要性漸漸式微;框架的橫空出世,也無疑為前端工程帶來的巨大的轉變。從原先在乎檔案功能的關注點分離,變成組件權責單一的關注點分離;從單純的頁面呈現,到越來越複雜的頁面流轉、資料串接。
當前端開發者社群的能量越來越強大,也代表著網頁前端需要接手處理的事情越來繁雜;幸好得以透過框架強大的功能,大幅度的減輕開發者的負擔。當然,框架同時也代表著限制,唯有理解框架背後的核心思想,才能在限制中找到最適合的方向。
從沒有框架時可能的問題,到框架帶來的許多優點,希望能藉由本文的內容,幫助讀者您理解框架的本質,以及現在的開發者為什麼需要框架。
那麼今天就先到這邊啦,明天我們將接續框架的主題繼續前進~
筆者
Gary
半路出家網站工程師;半生熟的前端加上一點點的後端。
喜歡音樂,喜歡學習、分享,也喜歡當個遊戲宅。相信一切安排都是最好的路。